﻿//*** Guidance generated code ***//
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Web.Mvc;

using ProjectBase.Core;
using ProjectBase.Core.Utils;
using ProjectBase.Core.Web;
using ProjectBase.Mvc;
using ProjectBase.Mvc.Controllers;
using ProjectBase.Mvc.Models;

using $prjsn$.Common;

namespace $safeprojectname$
{
    /// <summary>
    ///     MasterPageSelector AOP filter extendes the UrlHelper RoutData.Values.
    ///     Every window than can keep information about currently using masterpage
    ///     and append that information to any link or form action for the next response rendering
    /// </summary>
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
    public class MasterPageSelectorAttribute : ActionFilterAttribute, IPublishingReporter
    {
        #region OnAction execute
        /// <summary>
        ///     Extends the RouteData Values collection with
        ///     intended parameters.
        ///     Those will be appended to every URL generated viw AopUrlHelper,
        ///     which is passed under to every controller, ViewPage and ViewControl
        /// </summary>
        /// <param name = "filterContext"></param>
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            this.PublishInfo(() => "OnActionExecuting--started-MasterPageSelector");

            var dictionary = FindExtendingParameters(filterContext);
            AppendExtendingParameters(filterContext, dictionary);
            SetMasterNameToModel(filterContext);
            base.OnActionExecuting(filterContext);

            this.PublishInfo(() => "OnActionExecuting-finished-MasterPageSelector");
        }
        #endregion OnAction execute

        #region OnResult execute
        /// <summary>
        ///     Handles the MasterPage selecting
        ///     and filling the Selectors list
        ///     (this AOP is applied only on the Last result - which is in fact rendered view.)
        /// </summary>
        /// <param name = "filterContext"></param>
        public override void OnResultExecuting(ResultExecutingContext filterContext)
        {
            this.PublishInfo(() => "OnResultExecuting--started-MasterPageSelector");

            SetMasterNameToView(filterContext); // set Layout property to current View
            SetMasterPageOptionsList(filterContext); // filter object contains combo for MasterPage targets
            SetMasterPageSelectors(filterContext); // Render icons to switch current Layout
            SetListModel(filterContext); // ListView links should follow select MP, not the currently selected one
            base.OnResultExecuting(filterContext);

            this.PublishInfo(() => "OnResultExecuting-finished-MasterPageSelector");
        }
        #endregion OnResult execute

        #region Extend URL Values
        /// <summary>
        ///     Converts "known"!!! QueryString information
        ///     into the dictionary of (key, value) which should be appended to every new URL generated via AopUrlHelper
        /// </summary>
        /// <param name = "filterContext"></param>
        /// <returns></returns>
        protected virtual IDictionary<string, object> FindExtendingParameters(ControllerContext filterContext)
        {
            var dictionary = new Dictionary<string, object>();

            string selectedMasterPage;

            // I. Find MasterPage selector in querystring or routeValues
            if (!FindMasterPageValue(filterContext, out selectedMasterPage))
            {
                return dictionary;
            }

            // II. Are there more selectors? use the first, other then Async ("asc")
            var selectors = selectedMasterPage.Split(',');
            selectedMasterPage = selectors
                .FirstOrDefault(s => !s.IsEqual(Const.MasterPage.ValueAsync))
                ?? Const.MasterPage.ValueAsync;

            var controller = filterContext.Controller as IWebController<IViewModel>;
            if (controller.Is())
            {
                controller.TempData[AopUrlHelper.RouteValuePrefix + Const.MasterPage.KeyMasterPage] =
                    selectors.Length > 1 ? Const.MasterPage.ValueAsync : selectedMasterPage;
            }

            // III. Extend RouteValues to inject selecte MasterPage selector
            switch (selectedMasterPage)
            {
                // I. Printable
                case Const.MasterPage.ValuePrintable:
                    dictionary[Const.MasterPage.KeyMasterPage] = Const.MasterPage.ValuePrintable;
                    break;
                // II. Detail
                case Const.MasterPage.ValueDetail:
                    dictionary[Const.MasterPage.KeyMasterPage] = Const.MasterPage.ValueDetail;
                    break;

                // X. Default (not need at all - it is the default)
                default:
                    break;
            }

            // II. TODO - recognize other useful switches
            return dictionary;
        }

        /// <summary>
        ///     Appends all common route values (in dictionary)
        ///     to the Context.RouteData.Values
        ///     to be used on AopUrlHelper for extending queryString
        /// </summary>
        /// <param name = "filterContext"></param>
        /// <param name = "dictionary"></param>
        protected virtual void AppendExtendingParameters(ControllerContext filterContext,
            IDictionary<string, object> dictionary)
        {
            if (dictionary.IsEmpty())
            {
                return;
            }
            foreach (var pair in dictionary)
            {
                filterContext.RouteData.Values[AopUrlHelper.RouteValuePrefix + pair.Key] = pair.Value;
            }
        }
        #endregion before executing - resolve sent information

        #region MasterPages handling
        /// <summary>
        ///     Evaluates which MasterPage is asked for this view.
        ///     Result is stored in the MastrModel to be used anywhere else
        /// </summary>
        /// <param name = "filterContext"></param>
        protected virtual void SetMasterNameToModel(ActionExecutingContext filterContext)
        {
            var controller = filterContext.Controller as IWebController<IViewModel>;
            string selectedMasterPage;

            if (controller.IsNull()
                || !FindMasterPageValue(filterContext, out selectedMasterPage))
            {
                return; // default MasterPage will be used
            }
            controller.Model.MasterModel.CurrentMasterPageValue = Const.MasterPage.ValueMasterDefault;

            // let's decide which MasterPage will be used
            switch (selectedMasterPage)
            {
                // I. Printable
                case Const.MasterPage.ValuePrintable:
                    controller.Model.MasterModel.CurrentMasterPageValue = Const.MasterPage.ValuePrintable;
                    break;
                // II. Detail
                case Const.MasterPage.ValueDetail:
                    controller.Model.MasterModel.CurrentMasterPageValue = Const.MasterPage.ValueDetail;
                    break;
                // III. Rich
                case Const.MasterPage.ValueRich:
                    controller.Model.MasterModel.CurrentMasterPageValue = Const.MasterPage.ValueRich;
                    break;
                // IV. Async
                case Const.MasterPage.ValueAsync:
                    controller.Model.MasterModel.CurrentMasterPageValue = Const.MasterPage.ValueAsync;
                    break;
            }
        }

        /// <summary>
        ///     Checks if the MasterPage should be set to Printable or to Detail
        ///     or left default at all.
        ///     This action affects the ViewResult and its corresponding MasterModel setting for CSS
        /// </summary>
        /// <param name = "filterContext"></param>
        protected virtual void SetMasterNameToView(ResultExecutingContext filterContext)
        {
            var viewResult = filterContext.Result as ViewResult;
            if (viewResult.IsNull() || !viewResult.HasResultMasterPage())
            {
                return; // no MasterPage can be applied
            }

            var controller = filterContext.Controller as IWebController<IViewModel>;
            string selectedMasterPage;

            if (controller.IsNull() || !FindMasterPageValue(filterContext, out selectedMasterPage))
            {
                return; // default MasterPage will be used
            }

            // let's decide which MasterPage will be used
            switch (selectedMasterPage)
            {
                // I. Printable
                case Const.MasterPage.ValuePrintable:
                    viewResult.MasterName = Const.MasterPage.Names.Printable;
                    break;
                // II. Detail
                case Const.MasterPage.ValueDetail:
                    viewResult.MasterName = Const.MasterPage.Names.Detail;
                    break;
                // III. Rich
                case Const.MasterPage.ValueRich:
                    viewResult.MasterName = Const.MasterPage.Names.Rich;
                    break;
                // IV. Async
                case Const.MasterPage.ValueAsync:
                    viewResult.MasterName = Const.MasterPage.Names.Async;
                    break;
                case Const.MasterPage.ValueContent:
                    viewResult.MasterName = Const.MasterPage.Names.Content;
                    break;
            }
        }

        /// <summary>
        ///     Fills the Options list with available MasterPages
        ///     to be used e.g for combobox in the AdjustListWC
        /// </summary>
        /// <param name = "filterContext"></param>
        protected virtual void SetMasterPageOptionsList(ResultExecutingContext filterContext)
        {
            var model = filterContext.Controller.ViewData.Model as IFilterModel<IFilter>;
            if (model.Is())
            {
                // I. The Name of the MasterPage used for Links on the List
                model.Filter.MasterPageOptionsList = MasterPageOptionsList;
            }
        }

        /// <summary>
        ///     Searches Query string for MasterPage value.
        ///     If not found, searches controller.TempData for previously stored data
        /// </summary>
        /// <param name = "filterContext"></param>
        /// <param name = "selectedMasterPage"></param>
        /// <returns></returns>
        protected virtual bool FindMasterPageValue(ControllerContext filterContext, out string selectedMasterPage)
        {
            var controller = filterContext.Controller as IWebController<IViewModel>;
            var queryString = filterContext.RequestContext.HttpContext.Request.QueryString;

            selectedMasterPage = string.Empty;

            // Is there previously stored MP value in TEMP?
            if (controller.Is()
                && controller.TempData.ContainsKey(AopUrlHelper.RouteValuePrefix
                                                   + Const.MasterPage.KeyMasterPage))
            {
                selectedMasterPage = controller
                    .TempData[AopUrlHelper.RouteValuePrefix + Const.MasterPage.KeyMasterPage]
                    .ToString();
                return true;
            }

            // Is there MP in QueryString
            if (queryString.AllKeys.Contains(Const.MasterPage.KeyMasterPage))
            {
                selectedMasterPage = queryString[Const.MasterPage.KeyMasterPage];
                return true;
            }

            return false;
        }

        protected virtual void SetMasterPageSelectors(ResultExecutingContext filterContext)
        {
            var model = filterContext.Controller.ViewData.Model as Common.Models.ILayoutModel;
            if (model.IsNull())
            {
                return;
            }

            // I. Detail
            var isDetail = model.CurrentViewIs(Const.MasterPage.ValueDetail);

            model.MasterPageSelectors.Add(
                new SelectMasterPageItem
                {
                    ActionPath = CreateSelectorActionPath(filterContext
                        , isDetail ? Const.MasterPage.ValueMasterDefault : Const.MasterPage.ValueDetail),
                    ImagePath = ResourceManager.CreateUrl(Const.MasterPage.Common.ImageFolderPath +
                                                          (isDetail ? "detailoff.gif" : "detail.gif")),
                    ImageAlterName = isDetail ? Const.MasterPage.View.Default : Const.MasterPage.View.Detail,
                });

            // I. Printable
            var isPrint = model.CurrentViewIs(Const.MasterPage.ValuePrintable);

            model.MasterPageSelectors.Add(
                new SelectMasterPageItem
                {
                    ActionPath = CreateSelectorActionPath(filterContext
                        , isPrint ? Const.MasterPage.ValueMasterDefault : Const.MasterPage.ValuePrintable),
                    ImagePath = ResourceManager.CreateUrl(Const.MasterPage.Common.ImageFolderPath +
                                                          (isPrint ? "printoff.gif" : "print.gif")),
                    ImageAlterName = isPrint ? Const.MasterPage.View.Default : Const.MasterPage.View.Printable,
                });
        }

        protected virtual string CreateSelectorActionPath(ResultExecutingContext filterContext, string masterName)
        {
            var originalAction = filterContext.HttpContext.Request.Url.AbsoluteUri;
            var routeData = originalAction.GetRouteData();
            routeData.Values[Const.MasterPage.KeyMasterPage] = masterName;

            var action = routeData.Values[Str.Common.Action].ToString();
            var controller = filterContext.Controller as IWebController<IViewModel>;
            return controller.Url.Action(action, routeData.Values);
        }
        #endregion MasterPages handling

        #region IListModel handling
        /// <summary>
        ///     Extends the List model with selected MasterPage settings
        /// </summary>
        /// <param name = "filterContext"></param>
        protected virtual void SetListModel(ResultExecutingContext filterContext)
        {
            var controller =
                filterContext.Controller as IWebController<IEntityModel<IPersistentObject, IFilter>>;
            if (controller.IsNull()
                || controller.Model.ListModel.IsNull()
                || controller.Model.Filter.TargetMasterPageForLink.IsEmpty())
            {
                return;
            }
            var selctedMasterPage = controller.Model.Filter.TargetMasterPageForLink.IsNotEmpty()
                    ? controller.Model.Filter.TargetMasterPageForLink
                    : controller.Model.MasterModel.CurrentMasterPageValue;

            var deleteMasterPage = controller.Model.MasterModel.CurrentMasterPageValue ?? string.Empty;
            var listModel = controller.Model.ListModel;

            // I. List rows will be rendered (when clicked and navigated) in selected MasterPage
            listModel.AdditionalParameters[Const.MasterPage.KeyMasterPage] = selctedMasterPage;

            if (!deleteMasterPage.IsEqual(Const.MasterPage.ValueAsync))
            {
                // II. List Delete button should result in currently used MasterPage
                listModel.AdditionalDeleteParameters[Const.MasterPage.KeyMasterPage] = deleteMasterPage;
            }
        }
        #endregion IListModel handling

        #region static MasterPage option list
        protected static readonly IDictionary<string, string> MasterPageOptionsList = new Dictionary<string, string>
        {
            {string.Empty, Str.WebControls.Common.NotFound},
            {Const.MasterPage.ValueMasterDefault, Const.MasterPage.View.Default},
            {Const.MasterPage.ValueDetail, Const.MasterPage.View.Detail},
            {Const.MasterPage.ValuePrintable, Const.MasterPage.View.Printable},
        };
        #endregion static MasterPage option list
    }
}